//--------------------------------------------------------------------------------
// This file is a portion of the D2D1Renderer.  It is distributed
// under the MIT License, available in the root of this distribution and 
// at the following URL:
//
// http://www.opensource.org/licenses/mit-license.php
//
// Copyright (c) Mirco Timmermann
//
//--------------------------------------------------------------------------------
#include "DisplayObjectContainer.h"


namespace D2 {

DisplayObjectContainer::DisplayObjectContainer() {
	_x = 0;
	_y = 0;
	_width = 0;
	_height = 0;

	_mouseAvoidingEnabled = false;
	_disableSelfContainerHit = false;
}

DisplayObjectContainer::DisplayObjectContainer(int x, int y, int width, int height) {
	_x = x;
	_y = y;
	_width = width;
	_height = height;

	_mouseAvoidingEnabled = false;
	_disableSelfContainerHit = false;
}

DisplayObjectContainer::~DisplayObjectContainer(void) {
	//remove all children from his parent.
	removeAllChildren();

	//destroy all childs.
	/*
	if(_onDestroyAutoDeleteChilds) {
		for(int i = _children.size()-1; i >= 0; i--) {
			DisplayObject* displayObject = _children.at(i);
			delete displayObject;
		}
	}
	*/
}

bool DisplayObjectContainer::Draw(Direct2DContext* ctx, D2D1::Matrix3x2F* mtx) {
		for(unsigned int i = 0; i < _children.size(); i++) {
		DisplayObject* child = _children[i];

		//
		//Event dataEvent(Event::ENTER_FRAME, this);
		//child->dispatchEvent(dataEvent);
		//

		if(!child->visible) continue;

		D2D1::Matrix3x2F local = child->getMatrix();
		D2D1::Matrix3x2F new_mtx = local * (*mtx);

		ctx->setTransform(new_mtx._11, new_mtx._12, new_mtx._21, new_mtx._22, new_mtx._31, new_mtx._32);

		child->beginClip(ctx);
		child->Draw(ctx, &new_mtx);
		child->endClip(ctx);
	}

    return true;
}

///////////////////////////////////////////////////////////////////////////////////////////////
//                                  CONTAINER FUNCTIONS                                      //
///////////////////////////////////////////////////////////////////////////////////////////////

DisplayObject* DisplayObjectContainer::addChild(DisplayObject* displayObject) {
	if(displayObject == 0) return displayObject;

    if(displayObject->_parent != 0) { 
		displayObject->_parent->removeChild(displayObject); 
	}
    displayObject->_parent = this;

	_children.push_back(displayObject);

	//
	Event dataEvent(Event::ADDED, this);
	displayObject->OnAdded(dataEvent);
	displayObject->dispatchEvent(dataEvent);
	//

	_bubbleNotifyAdded(displayObject);

	return displayObject;
}

/*
* adds a child at a specific position.
*/
void DisplayObjectContainer::addChildAt(DisplayObject* displayObject, unsigned int index) {
	//if the displayObject has a parent break.
	if(displayObject->_parent != 0) {
#ifdef _DEBUG
		//MessageBox(NULL, "addChildAt::DisplayObject has already a parent. Remove it first from his parent to apply it to a new one.", "DisplayObjectContainer", 0);
#endif
		return;
	}
	//add the parent to the child.
	displayObject->_parent = this;
	_children.insert(_children.begin() + index, displayObject);

	//
	Event dataEvent(Event::ADDED, this);
	displayObject->OnAdded(dataEvent);
	displayObject->dispatchEvent(dataEvent);
	//

	_bubbleNotifyAdded(displayObject);
}

/* (bubbling)
*
*/
void DisplayObjectContainer::_bubbleNotifyAdded(DisplayObject* displayObject) {
	if(this->_parent != 0) {
		this->_parent->_bubbleNotifyAdded(displayObject);
	}
}

/* (bubbling)
* reaches a displayObject to the next higher parent until the hightest parent (root) is reached, for notify that the displayObject was removed.
*/
void DisplayObjectContainer::_bubbleNotifyRemoved(DisplayObject* displayObject) {
	if(this->_parent != 0) {
		this->_parent->_bubbleNotifyRemoved(displayObject);
	}
}

bool DisplayObjectContainer::removeChild(DisplayObject* displayObject, bool del) {
	for(int i = _children.size()-1; i >= 0; i--) {
		DisplayObject* currentDisplayObject = _children.at(i);
		if(currentDisplayObject == displayObject)  {
			displayObject->_parent = 0;
			_children.erase(_children.begin() + i);

			//
			Event dataEvent(Event::REMOVED, this);
			displayObject->OnRemoved(dataEvent);
			displayObject->dispatchEvent(dataEvent);
			//

			_bubbleNotifyRemoved(displayObject);

			if(del) {
				delete displayObject;
			}

			return true;
		}
	}
	//MessageBox(NULL, "removeChild::DisplayObjectContainer doesn't contains the DisplayObject", "DisplayObjectContainer", 0);
	return false;
}

bool DisplayObjectContainer::removeChildAt(unsigned int index, bool del) {
	if(index >= _children.size()) {
		//MessageBox(NULL, "removeChildAt::index is out of bounds", "DisplayObjectContainer", 0);
		return false;
	}

	DisplayObject* displayObject = _children.at(index);
	displayObject->_parent = 0;
	_children.erase(_children.begin() + index);

	//
	Event dataEvent(Event::REMOVED, this);
	displayObject->OnRemoved(dataEvent);
	displayObject->dispatchEvent(dataEvent);
	//

	_bubbleNotifyRemoved(displayObject);
	
	if(del) {
		displayObject->Destroy();
	}

	return true;
}

/*
* removes all childrens.
*/
void DisplayObjectContainer::removeAllChildren(bool recursive, bool del) {
	for(int i = _children.size()-1; i >= 0; i--) {
		DisplayObject* child = _children.at(i);

		//
		Event dataEvent(Event::REMOVED, this);
		child->OnRemoved(dataEvent);
		child->dispatchEvent(dataEvent);
		//

		_bubbleNotifyRemoved(child);

		if(recursive) {
			DisplayObjectContainer* container = dynamic_cast<DisplayObjectContainer*>(child);
			if(container != 0) {
				container->removeAllChildren(recursive, del);
			}
		}

		if(del) {
			child->Destroy();
		} else {
			child->_parent = 0;
		}
		
	}

	_children.clear();
}

int DisplayObjectContainer::getNumChildren() {
	return _children.size();
}

DisplayObject* DisplayObjectContainer::getChildAt(unsigned int index) {
	if(index >= _children.size()) return 0;

	return _children[index];
}

DisplayObject* DisplayObjectContainer::_getObjectsUnderPoint(int mouseX, int mouseY) {
	bool selfContainerHit = false;
	selfContainerHit = this->_hittest_boundingBox(mouseX, mouseY);

	 //break the search if the hit was outside of the containers size.
	if(_mouseAvoidingEnabled) {
		if(!selfContainerHit) {
			return 0;
		}
	}

	for(int i = getNumChildren()-1; i >= 0; i--) {
		DisplayObject* child = getChildAt(i);

		if(!child->isVisible() || !child->mouseEnabled) continue;

		DisplayObjectContainer* displayObjectContainer = dynamic_cast<DisplayObjectContainer*>(child);
		if(displayObjectContainer != 0) { //child instanceof Container
			DisplayObject* result = 0;

			result = displayObjectContainer->_getObjectsUnderPoint(mouseX, mouseY);
			if(result != 0) return result;
		} else {
			bool hit = child->_hittest_boundingBox(mouseX, mouseY);
			if(!hit) continue;

			return child;
		}
	}

	if(!_disableSelfContainerHit) {
		if(selfContainerHit) return this;
	}

	return 0;
}

/*
* returns the total height of the DisplayObject. 
* Means including the childs.
*/
float DisplayObjectContainer::GetTotalHeight() {
	float totalHeight = 0;
        
    float l = getNumChildren();
	for(int i = 0; i < l; i++) {
		DisplayObject* child = _children.at(i);

		float posY = child->_y + child->_height;

		if(posY > totalHeight) {
			totalHeight = posY;
		}
	}
            
	return totalHeight;
}

float DisplayObjectContainer::GetTotalWidth() {
	float totalWidth = 0;
        
    float l = getNumChildren();
	for(int i = 0; i < l; i++) {
		DisplayObject* child = _children.at(i);

		float posX = child->_x + child->_width;

		if(posX > totalWidth) {
			totalWidth = posX;
		}
	}
            
	return totalWidth;
}

//////////////

void DisplayObjectContainer::toForeground(DisplayObject* displayObject) {
	for(int i = _children.size()-1; i >= 0; i--) {
		DisplayObject* currentDisplayObject = _children.at(i);
		if(currentDisplayObject == displayObject)  {
			_children.erase(_children.begin() + i);
			_children.push_back(displayObject);
			return;
		}
	}
}

}